using namespace std;
#include <iostream>
#include <vector>
#include <cstdlib>
#include <ctime>
#include <cassert>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include "Task.hpp"

#define PROCESS_NUM 5

//目的：验证管道通信派发任务(一个父进程，生成多个子进程，父进程只打开写，子进程只打开读，从而实现父进程派发任务，子进程执行)

int waitCommand(int wait_fd)
{
    //初始化命令为4个字节
    uint32_t command =0;
    //读取这个命令的长度，然后判断其是否符合要求
    //从子进程wait_fd中读取到command中，同时规定必须读取到相应的长度
    ssize_t s = read(wait_fd,&command,sizeof(command));
    assert(s==sizeof(uint32_t));//如果发送的不是四个字节，则证明数据发送错误
    return command;
}

//发送并唤醒进程
void sendAndWakeup(pid_t who, int fd, uint32_t command)
{
    write(fd, &command, sizeof(command));
    cout << "main process: call process " << who << " execute " << desc[command] << " through " << fd << endl;
}

int main()
{
    load();
    vector<pair<pid_t,int>> slots;
    //创建多个子进程
    for(int i =0;i<PROCESS_NUM;i++)
    {
        //管道创建
        int pipefd[2]={0};
        int n = pipe(pipefd);
        assert(n==0);
        (void)n; // 防止n被断言

        pid_t id = fork();
        assert(id != -1);
        //子进程负责读取；关闭子进程的读取命令
        if(id ==0)
        {
            close(pipefd[1]);
            while (true)
            {
                //等命令，如果没有接收到命令，则阻塞
                int command = waitCommand(pipefd[0]);
                //执行对应的命令
                if(command>=0&&command < handler_size())
                {
                    //获得命令码后，利用callbacks调用相应的函数接口
                    callbacks[command]();
                }
                else 
                {
                    cout<<"非法command"<<command<<endl;
                }
                
            }
            exit(1);
        }
        //父进程负责写入，关闭其读端
        close(pipefd[0]);
        slots.push_back(pair<pid_t ,int>(id,pipefd[1]));
    }

    //父进程向子进程派发任务
    srand((unsigned long)time(nullptr)^getpid()^2115671L);//数据变的更随机
    while(true)
    {
        //随机选择任务和进程执行
        int command = rand()%handler_size();
        int choice = rand()%slots.size();
        //将任务交给制定的进程去执行
        sendAndWakeup(slots[choice].first,slots[choice].second,command);
        sleep(1);
    }

    //关闭fd，多有子进程退出
    for(const auto&slot:slots)
    {
        close(slot.second);
    }

    //回收所有子进程的信息
    for(const auto&slot:slots)
    {
        waitpid(slot.first,nullptr,0);
    }
    // return 0;
}